using System;
using System.Windows.Forms;
using CSW.Framework.Common;
using CSW.Framework.UI.Binding;
using DevExpress.XtraEditors;

namespace CSW.Framework.UI.DevEx.Binding
{
    /// <summary>
    /// A wrapper class for binding <see cref="TextEdit"/> objects with <see cref="Binder"/>.
    /// </summary>
    public class BindableTextEdit : IBindableControl
    {
        private readonly TextEdit m_TextEdit;
        private readonly bool m_ValidateOnChanged;
        private readonly DisplayConverter m_DisplayConverter;

        /// <summary>
        /// Initializes a new instance of the <see cref="BindableTextEdit"/> class.
        /// </summary>
        /// <param name="textEdit">The <see cref="TextEdit"/> control to wrap.</param>
        public BindableTextEdit(TextEdit textEdit)
            : this(textEdit, false, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BindableTextEdit"/> class.
        /// </summary>
        /// <param name="textEdit">The <see cref="TextEdit"/> control to wrap.</param>
        /// <param name="validateOnChanged">if set to <c>true</c> [validate on changed].</param>
        public BindableTextEdit(TextEdit textEdit, bool validateOnChanged)
            : this(textEdit, validateOnChanged, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BindableTextEdit"/> class.
        /// </summary>
        /// <param name="textEdit">The <see cref="TextEdit"/> control to wrap.</param>
        /// <param name="displayConverter">The display converter.</param>
        public BindableTextEdit(TextEdit textEdit, DisplayConverter displayConverter)
            : this(textEdit, false, displayConverter)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BindableTextEdit"/> class.
        /// </summary>
        /// <param name="textEdit">The <see cref="TextEdit"/> control to wrap.</param>
        /// <param name="displayFormat">The display format.</param>
        public BindableTextEdit(TextEdit textEdit, string displayFormat)
            : this(textEdit, false, new DisplayConverter(displayFormat))
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BindableTextEdit"/> class.
        /// </summary>
        /// <param name="textEdit">The <see cref="TextEdit"/> control to wrap.</param>
        /// <param name="validateOnChanged">if set to <c>true</c> [validate on changed].</param>
        /// <param name="displayConverter">The display converter.</param>
        private BindableTextEdit(TextEdit textEdit, bool validateOnChanged, DisplayConverter displayConverter)
        {
            Guard.ArgumentNotNull(textEdit, "textEdit");

            if (textEdit is MemoEdit)
                throw new InvalidOperationException(string.Format("Use BindableMemoEdit for control '{0}'.", textEdit.Name));

            m_TextEdit = textEdit;
            m_ValidateOnChanged = validateOnChanged;

            m_TextEdit.Validated += TextEdit_Validated;

            if (validateOnChanged)
                m_TextEdit.TextChanged += TextEdit_Validated;

            m_DisplayConverter = displayConverter;
            if (m_DisplayConverter != null)
                m_DisplayConverter.UpdateDisplay += DisplayConverter_UpdateDisplay;
        }

        private void DisplayConverter_UpdateDisplay(object sender, EventArgs e)
        {
            string displayText = m_DisplayConverter.GetFormattedText(Value);
            m_TextEdit.Text = displayText;
        }

        private void TextEdit_Validated(object sender, EventArgs e)
        {
            EventHandler validated = Validated;
            if (validated != null)
                validated(sender, e);
        }

        #region IBindableControl Members

        /// <summary>
        /// Occurs when the control has validated.
        /// </summary>
        public event EventHandler Validated;

        /// <summary>
        /// Sends the <see cref="Validated"/> event.
        /// </summary>
        public void SendValidated()
        {
            EventHandler validated = Validated;
            if (validated != null)
                validated(this, EventArgs.Empty);
        }

        /// <summary>
        /// Gets the control this object wraps.
        /// </summary>
        /// <value>The control this object wraps.</value>
        public Control Control
        {
            get { return m_TextEdit; }
        }

        /// <summary>
        /// Gets or sets the value of the control.
        /// </summary>
        /// <value>The value of the control.</value>
        public object Value
        {
            get 
            {
                if (m_DisplayConverter != null)
                    return m_DisplayConverter.GetUnformattedText(m_TextEdit.Text);
                else
                    return m_TextEdit.Text; 
            }
            set
            {
                object newValue = value ?? string.Empty;
                if (!m_TextEdit.Text.Equals(newValue))
                {
                    if (m_DisplayConverter != null)
                        m_TextEdit.Text = m_DisplayConverter.GetFormattedText(newValue);
                    else
                        m_TextEdit.Text = newValue.ToString();
                }
            }
        }

        /// <summary>
        /// Gets the name of the control.
        /// </summary>
        /// <value>The name of the control.</value>
        public string Name
        {
            get { return m_TextEdit.Name; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the control is enabled.
        /// </summary>
        /// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value>
        public bool Enabled
        {
            get { return m_TextEdit.Enabled; }
            set { m_TextEdit.Enabled = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the control is read only.
        /// </summary>
        /// <value><c>true</c> if read only; otherwise, <c>false</c>.</value>
        public bool ReadOnly
        {
            get { return m_TextEdit.Properties.ReadOnly; }
            set 
            { 
                m_TextEdit.Properties.ReadOnly = value;
                m_TextEdit.TabStop = !value;
            }
        }

        /// <summary>
        /// Focuses the control.
        /// </summary>
        public void Focus()
        {
            m_TextEdit.Focus();
        }

        /// <summary>
        /// Gets the tab index.
        /// </summary>
        /// <value>The tab index.</value>
        public int TabIndex
        {
            get { return m_TextEdit.TabIndex; }
        }

        /// <summary>
        /// Gets or sets the maximum string length.
        /// </summary>
        /// <value>The maximum string length.</value>
        public int MaxLength
        {
            get { return m_TextEdit.Properties.MaxLength; }
            set { m_TextEdit.Properties.MaxLength = value; }
        }

        #endregion

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Validated = null;

            m_TextEdit.Validated -= TextEdit_Validated;

            if (m_ValidateOnChanged)
                m_TextEdit.TextChanged -= TextEdit_Validated;

            if (m_DisplayConverter != null)
            {
                m_DisplayConverter.UpdateDisplay -= DisplayConverter_UpdateDisplay;
            }
        }
    }
}